﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Sustainalytics.ClientDW.DataAccessLayer;
using Sustainalytics.ClientDW.Entities;
using Sustainalytics.Events.DataAccessLayer;
using Sustainalytics.RP.DataAccessLayer;
using Sustainalytics.ClientDW.Tracing;
using Sustainalytics.DataLayer;

namespace Sustainalytics.DW.Synchronization
{   
    public class DWSync : IDWSync
    {
        private static IDWSync _instance;
        private static IClientDwRepository _clientDwRepository;
        private static ICorporateDataReader _corporateDataReader;
        private static IEventsDataReader _eventsDataReader;
        private static IRpDataReader _rpDataReader;
        private static ICompanyScreeningRepository _companyScreeningRepository;

        private DWSync() { }

        public static IDWSync GetInstance(IClientDwRepository clientDwRepository, ICorporateDataReader corporateDataReader, IRpDataReader rpDataReader, IEventsDataReader eventsDataReader, ICompanyScreeningRepository companyScreeningRepository)
        {
            if (_instance != null) return _instance;

            if (clientDwRepository == null)
                throw new ArgumentException("An instance of type IAccountsSync must be provided in order to create an IDWSync instance!");

            if (corporateDataReader == null)
                throw new ArgumentException("An instance of type ICorporateDataReader must be provided in order to create an IDWSync instance!");

            if (rpDataReader == null)
                throw new ArgumentException("An instance of type IRpDataReader must be provided in order to create an IDWSync instance!");

            if (eventsDataReader == null)
                throw new ArgumentException("An instance of type IEventsDataReader must be provided in order to create an IDWSync instance!");

            if (companyScreeningRepository == null)
                throw new ArgumentException("An instance of type ICompanyScreeningRepository must be provided in order to create an IDWSync instance!");

            _clientDwRepository = clientDwRepository;
            _corporateDataReader = corporateDataReader;
            _rpDataReader = rpDataReader;
            _eventsDataReader = eventsDataReader;
            _companyScreeningRepository = companyScreeningRepository;

            _instance = new DWSync();

            return _instance;
        }

        public void StartManualSync()
        {
            try
            {
                var sw = Stopwatch.StartNew();

                DateTime? lastSyncTime = null; //_clientDwRepository.GetLastSyncTime();

                //var syncLog = _clientDwRepository.AddNewEntryInSyncLog();

                var companyIds = _corporateDataReader.ListCompanyIds(lastSyncTime);

                SendMessage(string.Format("Updated companies: {0}", companyIds.Count));

                foreach (var guids in companyIds.Batch(1000))
                {
                    ClientDWEventSource.Log.StartSyncEntity(typeof(CorporateData).Name);

                    var corporateDatas = _corporateDataReader.ListCorporateDatas(guids);

                    if (corporateDatas.Any())
                        _clientDwRepository.SaveCorporateData(corporateDatas);

                    ClientDWEventSource.Log.StopSyncEntity(typeof(CorporateData).Name, corporateDatas.Count);


                    ClientDWEventSource.Log.StartSyncEntity(typeof(FinancialData).Name);

                    var financialDatas = _corporateDataReader.ListFinancialDatas(guids);
                    if (financialDatas.Any())
                        _clientDwRepository.SaveFinancialData(financialDatas);

                    ClientDWEventSource.Log.StopSyncEntity(typeof(FinancialData).Name, financialDatas.Count);


                    ClientDWEventSource.Log.StartSyncEntity(typeof(ReferenceData).Name);

                    var referenceDatas = _corporateDataReader.ListReferenceDatas(guids);
                    if (referenceDatas.Any())
                        _clientDwRepository.SaveReferenceData(referenceDatas);

                    ClientDWEventSource.Log.StopSyncEntity(typeof(ReferenceData).Name, referenceDatas.Count);


                    ClientDWEventSource.Log.StartSyncEntity(typeof(RPIndicator).Name);

                    var indicators = _rpDataReader.ListRpIndicators(guids);
                    if (indicators.Any())
                        _clientDwRepository.SaveRpIndicators(indicators);

                    ClientDWEventSource.Log.StopSyncEntity(typeof(RPIndicator).Name, indicators.Count);
                }

               if (companyIds.Any())
                {
                    var companyTypes = new List<ClientDW.Entities.CompanyType>
                    {
                        new ClientDW.Entities.CompanyType
                        {
                            Id = 1,
                            Name = "Type_A"
                        },
                        new ClientDW.Entities.CompanyType
                        {
                            Id = 2,
                            Name = "Type_B"
                        },
                        new ClientDW.Entities.CompanyType
                        {
                            Id = 3,
                            Name = "Private"
                        }
                    };
                    _clientDwRepository.SaveCompanyTypes(companyTypes);

                    ClientDWEventSource.Log.StartSyncEntity(typeof(ClientDW.Entities.Country).Name);
                    var countries = _corporateDataReader.ListCountries();
                    if (countries.Any())
                    _clientDwRepository.SaveCountries(countries);
                    ClientDWEventSource.Log.StopSyncEntity(typeof(ClientDW.Entities.Country).Name, countries.Count);

                    ClientDWEventSource.Log.StartSyncEntity(typeof(ClientDW.Entities.Region).Name);
                    var regions = _corporateDataReader.ListRegions();
                    if (regions.Any())
                        _clientDwRepository.SaveRegions(regions);
                    ClientDWEventSource.Log.StopSyncEntity(typeof(ClientDW.Entities.Region).Name, regions.Count);

                    ClientDWEventSource.Log.StartSyncEntity(typeof(RootPeerGroup).Name);
                    var rootPeerGroups = _corporateDataReader.ListRootPeerGroups();
                    if (rootPeerGroups.Any())
                        _clientDwRepository.SaveRootPeerGroups(rootPeerGroups);
                    ClientDWEventSource.Log.StopSyncEntity(typeof(RootPeerGroup).Name, rootPeerGroups.Count);

                    ClientDWEventSource.Log.StartSyncEntity(typeof(PeerGroup).Name);
                    var peerGroups = _corporateDataReader.ListPeerGroups();
                    if (peerGroups.Any())
                        _clientDwRepository.SavePeerGroups(peerGroups);
                    ClientDWEventSource.Log.StopSyncEntity(typeof(PeerGroup).Name, peerGroups.Count);

                    ClientDWEventSource.Log.StartSyncEntity(typeof(GICSSubIndustry).Name);
                    var subIndustries = _corporateDataReader.ListGICSSubIndustries();
                    if (subIndustries.Any())
                        _clientDwRepository.SaveGICSSubIndustries(subIndustries);
                    ClientDWEventSource.Log.StopSyncEntity(typeof(GICSSubIndustry).Name, subIndustries.Count);

                    ClientDWEventSource.Log.StartSyncEntity(typeof(GICSIndustry).Name);
                    var industries = _corporateDataReader.ListGICSIndustries();
                    if (industries.Any())
                        _clientDwRepository.SaveGICSIndustries(industries);
                    ClientDWEventSource.Log.StopSyncEntity(typeof(GICSIndustry).Name, industries.Count);

                    ClientDWEventSource.Log.StartSyncEntity(typeof(GICSIndustryGroup).Name);
                    var industryGroups = _corporateDataReader.ListGICSIndustryGroups();
                    if (industryGroups.Any())
                        _clientDwRepository.SaveGICSIndustryGroups(industryGroups);
                    ClientDWEventSource.Log.StopSyncEntity(typeof(GICSIndustryGroup).Name, industryGroups.Count);

                    ClientDWEventSource.Log.StartSyncEntity(typeof(GICSSector).Name);
                    var sectors = _corporateDataReader.ListGICSSectors();
                    if (sectors.Any())
                        _clientDwRepository.SaveGICSSectors(sectors);
                    ClientDWEventSource.Log.StopSyncEntity(typeof(GICSSector).Name, sectors.Count);

                    ClientDWEventSource.Log.StartSyncEntity(typeof(ClientDW.Entities.ISIN).Name);
                    var isins = _corporateDataReader.ListISINs();
                    if (isins.Any())
                        _clientDwRepository.SaveISINs(isins);
                    ClientDWEventSource.Log.StopSyncEntity(typeof(ClientDW.Entities.ISIN).Name, isins.Count);

                    ClientDWEventSource.Log.StartSyncEntity(typeof(ClientDW.Entities.Sedol).Name);
                    var sedols = _corporateDataReader.ListSedols();
                    if (sedols.Any())
                        _clientDwRepository.SaveSedols(sedols);
                    ClientDWEventSource.Log.StopSyncEntity(typeof(ClientDW.Entities.Sedol).Name, sedols.Count);

                    ClientDWEventSource.Log.StartSyncEntity(typeof(Exchange).Name);
                    var exchanges = _corporateDataReader.ListExchanges();
                    if (exchanges.Any())
                        _clientDwRepository.SaveExchanges(exchanges);
                    ClientDWEventSource.Log.StopSyncEntity(typeof(Exchange).Name, exchanges.Count);

                    ClientDWEventSource.Log.StartSyncEntity(typeof(ClientDW.Entities.Ticker).Name);
                    var tickers = _corporateDataReader.ListTickers();
                    if (tickers.Any())
                        _clientDwRepository.SaveTickers(tickers);
                    ClientDWEventSource.Log.StopSyncEntity(typeof(ClientDW.Entities.Ticker).Name, tickers.Count);

                    ClientDWEventSource.Log.StartSyncEntity(typeof(ClientDW.Entities.CUSIP).Name);
                    var cusips = _corporateDataReader.ListCUSIPs();
                    if (cusips.Any())
                        _clientDwRepository.SaveCUSIPs(cusips);
                    ClientDWEventSource.Log.StopSyncEntity(typeof(ClientDW.Entities.CUSIP).Name, cusips.Count);


                    ClientDWEventSource.Log.StartSyncEntity(typeof(IndicatorTemplate).Name);

                    var indicatorTemplates = _rpDataReader.ListIndicatorTemplates();
                    if (indicatorTemplates.Any())
                    {
                        _clientDwRepository.SaveIndicatorTemplates(indicatorTemplates);

                        foreach (var indicatorTemplate in indicatorTemplates)
                        {
                            _companyScreeningRepository.UpdateCriteriaTypeFiveName(indicatorTemplate.Number, 
                                string.Format("{0} {1}", indicatorTemplate.Number, indicatorTemplate.Name));
                        }
                    }

                    ClientDWEventSource.Log.StopSyncEntity(typeof(IndicatorTemplate).Name, indicatorTemplates.Count);
                }

                Console.WriteLine("Corporate data synchronization completed successfully. Rows affected: {0}", companyIds.Count);
                SendMessage(string.Format("Corporate data synchronization completed successfully. Rows affected: {0}", companyIds.Count));

                ClientDWEventSource.Log.StartSyncEntity(typeof(ClientDW.Entities.EventIndicator).Name);

                var companyProfileIds = _eventsDataReader.ListCompanyProfileIds(lastSyncTime);

                foreach (var guids in companyProfileIds.Batch(500))
                {
                    var eventIndicators = _eventsDataReader.ListEventIndicators(guids);
                    if (eventIndicators.Any())
                        _clientDwRepository.SaveEventIndicators(eventIndicators);
                }

                Console.WriteLine("Event indicators synchronization completed successfully. Rows affected: {0}", companyProfileIds.Count * 28);
                SendMessage(string.Format("Event indicators synchronization completed successfully. Rows affected: {0}", companyProfileIds.Count * 28));

                ClientDWEventSource.Log.StopSyncEntity(typeof(ClientDW.Entities.EventIndicator).Name, companyProfileIds.Count * 28);


                //_clientDwRepository.UpdateSyncLog(syncLog);

                sw.Stop();

                Console.WriteLine("Synchronization completed successfully. Total time elapsed {0:#0.00} min",
                    sw.Elapsed.TotalMinutes);

                SendMessage(string.Format("Synchronization completed successfully. Total time elapsed {0:#0.00} min", sw.Elapsed.TotalMinutes));
            }
            catch (Exception e)
            {
                Console.WriteLine("Synchronization has failed! {0}", e);
                ClientDWEventSource.Log.Error(e.ToString());
            }
        }

        private static void SendMessage(string message)
        {
            SyncHub.HubContext.Clients.All.addMessage(string.Format("{0} {1}", DateTime.Now.ToString("HH:mm:ss"), message));
        }
    }
}
